Skip to content

feat: add first-party Tinybird analytics#2003

Open
richiemcilroy wants to merge 16 commits into
mainfrom
codex/first-party-analytics
Open

feat: add first-party Tinybird analytics#2003
richiemcilroy wants to merge 16 commits into
mainfrom
codex/first-party-analytics

Conversation

@richiemcilroy

@richiemcilroy richiemcilroy commented Jul 12, 2026

Copy link
Copy Markdown
Member

Summary

  • Add a strict, bounded first-party product analytics path for web and desktop.
  • Instrument signup, checkout, paid purchase, trial, organization expansion, and core recording events while disabling PostHog autocapture and session replay.
  • Add Tinybird Docker infrastructure, safe CI deployment, retry-safe aggregates, and read-only agent queries.

Validation

  • pnpm analytics:test — 146 tests passed
  • pnpm typecheck
  • cargo fmt --all -- --check
  • cargo check -p cap-desktop
  • cargo test -p cap-desktop posthog --lib — 11 tests passed
  • Biome, Actionlint, Docker Compose validation, and diff checks passed

Rollout gate

Explicit PostHog dual-write remains until 14 days of parity and a durable purchase outbox/replay path exist. Tinybird Local runtime build and fixture execution are delegated to this PR's analytics workflow.

Greptile Summary

This PR adds first-party Tinybird product analytics across web, desktop, and deployment tooling. The main changes are:

  • New /api/events ingestion path for bounded client analytics.
  • Product event normalization and shared event taxonomy.
  • Web and desktop instrumentation for signup, checkout, purchases, trials, organizations, and recording events.
  • Tinybird local, test, query, and deployment tooling.
  • CI workflow for validating and deploying analytics resources.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.
  • The previously risky analytics ingestion paths now have concrete request, token, actor, and event-shape checks.
  • The Tinybird deploy path now verifies the active workspace id before running deploy commands.

Important Files Changed

Filename Overview
apps/web/lib/analytics/request.ts Adds request validation, anonymous browser event allowlisting, server-only event rejection, and rate-limit key handling for product analytics ingestion.
apps/web/app/api/events/route.ts Adds the product analytics capture route with browser-token, actor, rate-limit, normalization, and append checks.
scripts/analytics/tooling.js Adds Tinybird project validation, local tooling, cloud deploy plans, and exact workspace identity verification before deployment.
.github/workflows/analytics.yml Adds CI validation and guarded production deployment for Tinybird analytics resources.
packages/analytics/src/index.ts Defines shared product analytics limits, event names, normalization, and row creation behavior.

Reviews (9): Last reviewed commit: "fix: parse Tinybird workspace identity" | Re-trigger Greptile

Context used:

  • Context used - CLAUDE.md (source)
  • Context used - AGENTS.md (source)

@richiemcilroy richiemcilroy marked this pull request as ready for review July 12, 2026 15:56
Comment thread apps/web/app/utils/product-analytics.ts Fixed
Comment thread scripts/analytics/tooling.js Fixed
Comment thread scripts/analytics/tooling.js Fixed
Comment thread apps/web/lib/analytics/request.ts Outdated
Comment thread apps/web/lib/analytics/request.ts
Comment thread scripts/analytics/tooling.js

@superagent-security superagent-security Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Superagent found 1 security concern(s).

Comment thread .github/workflows/analytics.yml Outdated
@superagent-security superagent-security Bot added the pr:flagged PR flagged for review by security analysis. label Jul 12, 2026
Comment thread apps/web/lib/analytics/request.ts Outdated
Comment thread scripts/analytics/tooling.js Outdated
@superagent-security superagent-security Bot removed the pr:flagged PR flagged for review by security analysis. label Jul 12, 2026
@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai, please re-review the PR

Comment thread apps/web/lib/analytics/request.ts Outdated
@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai, please re-review the PR

Comment thread apps/web/lib/analytics/request.ts
Comment thread apps/web/lib/analytics/request.ts
@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai, please re-review the PR

@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai, please re-review the PR

Comment thread apps/web/lib/analytics/request.ts Outdated
@superagent-security superagent-security Bot added the pr:flagged PR flagged for review by security analysis. label Jul 12, 2026
Comment thread apps/web/lib/analytics/request.ts
Comment thread apps/web/lib/analytics/request.ts
@superagent-security superagent-security Bot removed the pr:flagged PR flagged for review by security analysis. label Jul 12, 2026
@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai, please re-review the PR

Comment thread apps/web/lib/analytics/request.ts
@superagent-security superagent-security Bot added pr:flagged PR flagged for review by security analysis. and removed pr:flagged PR flagged for review by security analysis. labels Jul 12, 2026
Comment thread apps/web/lib/analytics/request.ts Outdated
@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai, please re-review the PR

Comment thread scripts/analytics/tooling.js Outdated
@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai, please re-review the PR

Comment thread apps/web/lib/analytics/request.ts Outdated
Comment thread scripts/analytics/tooling.js
Comment thread apps/web/app/api/events/route.ts
Comment thread scripts/analytics/tooling.js Outdated
Comment thread scripts/analytics/tooling.js Outdated

@superagent-security superagent-security Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Superagent found 1 security concern(s).

Comment thread scripts/analytics/tooling.js Outdated
@superagent-security superagent-security Bot added the pr:flagged PR flagged for review by security analysis. label Jul 12, 2026
@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai, please re-review the PR

@superagent-security superagent-security Bot removed the pr:flagged PR flagged for review by security analysis. label Jul 12, 2026
Comment on lines +92 to +100
const browserClaims =
hasExpectedBrowserAnalyticsMetadata(
requestMetadata,
allowedOrigins,
) &&
readProductAnalyticsBrowserTokenClaims(
readProductAnalyticsBrowserToken(headers.cookie),
serverEnv().NEXTAUTH_SECRET,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor robustness nit: if token claim parsing throws (bad secret, malformed cookie, etc) this will currently 500. I'd rather treat that as “no browser claims” and let it fall through to the normal BadRequest path.

Suggested change
const browserClaims =
hasExpectedBrowserAnalyticsMetadata(
requestMetadata,
allowedOrigins,
) &&
readProductAnalyticsBrowserTokenClaims(
readProductAnalyticsBrowserToken(headers.cookie),
serverEnv().NEXTAUTH_SECRET,
);
let browserClaims;
if (
hasExpectedBrowserAnalyticsMetadata(
requestMetadata,
allowedOrigins,
)
) {
try {
browserClaims = readProductAnalyticsBrowserTokenClaims(
readProductAnalyticsBrowserToken(headers.cookie),
serverEnv().NEXTAUTH_SECRET,
);
} catch {
browserClaims = undefined;
}
}

Comment on lines +432 to +434
if (result.error || result.status !== 0) {
throw new Error("Unable to verify Tinybird workspace identity.");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This failure path drops the actual stderr/stdout, which makes CI deploy debugging pretty painful. I’d include stderr (trimmed) in the thrown error message.

Suggested change
if (result.error || result.status !== 0) {
throw new Error("Unable to verify Tinybird workspace identity.");
}
if (result.error || result.status !== 0) {
const stderr = (result.stderr ?? "").toString().trim();
throw new Error(
stderr
? `Unable to verify Tinybird workspace identity: ${stderr}`
: "Unable to verify Tinybird workspace identity.",
);
}

@richiemcilroy richiemcilroy force-pushed the codex/first-party-analytics branch from a9b199c to fee7c3a Compare July 12, 2026 22:48
{
eventId,
eventName: event.eventName,
occurredAt: event.occurredAt ?? new Date().toISOString(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small robustness thing: occurredAt is passed through as-is, so a bad caller value will land as a bad timestamp in Tinybird. Might be worth normalizing/validating and falling back to now.

Suggested change
occurredAt: event.occurredAt ?? new Date().toISOString(),
occurredAt:
event.occurredAt && Number.isFinite(Date.parse(event.occurredAt))
? new Date(event.occurredAt).toISOString()
: new Date().toISOString(),

Comment on lines +49 to +55
const client = new PostHog(key, { host });
client.capture({
distinctId: event.distinctId,
event: event.eventName,
properties: event.properties,
});
await client.shutdown();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor cleanup: if client.capture throws, shutdown() won’t run. I’d wrap shutdown in a finally.

Suggested change
const client = new PostHog(key, { host });
client.capture({
distinctId: event.distinctId,
event: event.eventName,
properties: event.properties,
});
await client.shutdown();
const client = new PostHog(key, { host });
try {
client.capture({
distinctId: event.distinctId,
event: event.eventName,
properties: event.properties,
});
} finally {
await client.shutdown();
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants